001 /* 002 * Copyright 2006 Stephen J. McConnell. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.state; 020 021 import java.io.IOException; 022 import java.io.OutputStream; 023 import java.io.Writer; 024 import java.io.OutputStreamWriter; 025 026 import javax.xml.XMLConstants; 027 028 import net.dpml.state.Trigger.TriggerEvent; 029 030 /** 031 * Construct a state graph. 032 */ 033 public class StateEncoder 034 { 035 private static final String XML_HEADER = 036 "<?xml version=\"1.0\"?>"; 037 038 private static final String STATE_SCHEMA_URN = "link:xsd:dpml/lang/dpml-state#1.0"; 039 040 /** 041 * Externalize the part to XML. 042 * @param state the state graph to externalize 043 * @param output the output stream 044 * @exception IOException if an IO error occurs 045 */ 046 public void export( State state, OutputStream output ) throws IOException 047 { 048 final Writer writer = new OutputStreamWriter( output ); 049 050 writer.write( XML_HEADER ); 051 writer.write( "\n\n" ); 052 writer.write( "<state xmlns=\"" 053 + STATE_SCHEMA_URN 054 + "\"" 055 + "\n xmlns:xsi=\"" 056 + XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI 057 + "\"" ); 058 if( state.isTerminal() ) 059 { 060 writer.write( " terminal=\"true\">" ); 061 } 062 else 063 { 064 writer.write( ">" ); 065 } 066 writer.write( "\n" ); 067 writeBody( writer, state, "" ); 068 writer.write( "\n" ); 069 writer.write( "\n</state>" ); 070 writer.write( "\n" ); 071 writer.flush(); 072 output.close(); 073 } 074 075 /** 076 * Write the state. 077 * @param writer the stream writer 078 * @param state the state to externalize 079 * @param pad the pad offset 080 * @exception IOException if an IO error occurs 081 */ 082 public void writeState( Writer writer, State state, String pad ) throws IOException 083 { 084 if( isEmpty( state ) ) 085 { 086 return; 087 } 088 else 089 { 090 writer.write( "\n" + pad + "<state xmlns=\"" + STATE_SCHEMA_URN + "\"" ); 091 if( state.isTerminal() ) 092 { 093 writer.write( " terminal=\"true\"" ); 094 } 095 writer.write( ">" ); 096 writeBody( writer, state, pad + " " ); 097 writer.write( "\n" + pad + "</state>" ); 098 } 099 } 100 101 private void writeBody( Writer writer, State state, String pad ) throws IOException 102 { 103 Trigger[] triggers = state.getTriggers(); 104 Transition[] transitions = state.getTransitions(); 105 Operation[] operations = state.getOperations(); 106 Interface[] interfaces = state.getInterfaces(); 107 State[] states = state.getStates(); 108 109 writeTriggers( writer, triggers, pad ); 110 writeTransitions( writer, transitions, pad ); 111 writeOperations( writer, operations, pad ); 112 writeInterfaces( writer, interfaces, pad ); 113 writeStates( writer, states, pad ); 114 } 115 116 private void writeTriggers( Writer writer, Trigger[] triggers, String pad ) throws IOException 117 { 118 if( triggers.length == 0 ) 119 { 120 return; 121 } 122 else 123 { 124 for( int i=0; i<triggers.length; i++ ) 125 { 126 Trigger trigger = triggers[i]; 127 writeTrigger( writer, trigger, pad ); 128 } 129 } 130 } 131 132 private void writeTrigger( Writer writer, Trigger trigger, String pad ) throws IOException 133 { 134 TriggerEvent event = trigger.getEvent(); 135 writer.write( "\n" + pad + "<trigger event=\"" ); 136 writer.write( event.getName() ); 137 writer.write( "\">" ); 138 Action action = trigger.getAction(); 139 writeAction( writer, action, pad + " " ); 140 writer.write( "\n" + pad + "</trigger>" ); 141 } 142 143 private void writeTransitions( Writer writer, Transition[] transitions, String pad ) throws IOException 144 { 145 if( transitions.length == 0 ) 146 { 147 return; 148 } 149 else 150 { 151 for( int i=0; i<transitions.length; i++ ) 152 { 153 Transition transition = transitions[i]; 154 writeTransition( writer, transition, pad ); 155 } 156 } 157 } 158 159 private void writeOperations( Writer writer, Operation[] operations, String pad ) throws IOException 160 { 161 if( operations.length == 0 ) 162 { 163 return; 164 } 165 else 166 { 167 for( int i=0; i<operations.length; i++ ) 168 { 169 Operation operation = operations[i]; 170 writeOperation( writer, operation, pad ); 171 } 172 } 173 } 174 175 private void writeInterfaces( Writer writer, Interface[] interfaces, String pad ) throws IOException 176 { 177 if( interfaces.length == 0 ) 178 { 179 return; 180 } 181 else 182 { 183 for( int i=0; i<interfaces.length; i++ ) 184 { 185 Interface spec = interfaces[i]; 186 writeInterface( writer, spec, pad ); 187 } 188 } 189 } 190 191 private void writeStates( Writer writer, State[] states, String pad ) throws IOException 192 { 193 if( states.length == 0 ) 194 { 195 return; 196 } 197 else 198 { 199 for( int i=0; i<states.length; i++ ) 200 { 201 State state = states[i]; 202 writeNestedState( writer, state, pad ); 203 } 204 } 205 } 206 207 private void writeTransition( Writer writer, Transition transition, String pad ) throws IOException 208 { 209 String name = transition.getName(); 210 String target = transition.getTargetName(); 211 writer.write( "\n" + pad + "<transition name=\"" + name + "\" target=\"" + target + "\"" ); 212 Operation operation = transition.getOperation(); 213 if( null == operation ) 214 { 215 writer.write( "/>" ); 216 } 217 else 218 { 219 writer.write( ">" ); 220 writeOperation( writer, operation, pad + " " ); 221 writer.write( "\n" + pad + "</transition>" ); 222 } 223 } 224 225 private void writeOperation( Writer writer, Operation operation, String pad ) throws IOException 226 { 227 String name = operation.getName(); 228 String method = operation.getMethodName(); 229 writer.write( "\n" + pad + "<operation name=\"" + name + "\"" ); 230 if( null != method ) 231 { 232 writer.write( " method=\"" + method + "\"" ); 233 } 234 writer.write( "/>" ); 235 } 236 237 private void writeInterface( Writer writer, Interface spec, String pad ) throws IOException 238 { 239 String classname = spec.getClassname(); 240 writer.write( "\n" + pad + "<interface class=\"" + classname + "\"/>" ); 241 } 242 243 private void writeAction( Writer writer, Action action, String pad ) throws IOException 244 { 245 if( action instanceof Transition ) 246 { 247 Transition transition = (Transition) action; 248 writeTransition( writer, transition, pad ); 249 } 250 else if( action instanceof Operation ) 251 { 252 Operation operation = (Operation) action; 253 writeOperation( writer, operation, pad ); 254 } 255 else if( action instanceof Interface ) 256 { 257 Interface spec = (Interface) action; 258 writeInterface( writer, spec, pad ); 259 } 260 else if( action instanceof ExecAction ) 261 { 262 ExecAction exec = (ExecAction) action; 263 writeExecAction( writer, exec, pad ); 264 } 265 else if( action instanceof ApplyAction ) 266 { 267 ApplyAction apply = (ApplyAction) action; 268 writeApplyAction( writer, apply, pad ); 269 } 270 else 271 { 272 final String error = 273 "Unrecognized action class [" 274 + action.getClass().getName() 275 + "]."; 276 throw new IOException( error ); 277 } 278 } 279 private void writeExecAction( Writer writer, ExecAction action, String pad ) throws IOException 280 { 281 String id = action.getID(); 282 writer.write( "\n" + pad + "<exec id=\"" + id + "\"/>" ); 283 } 284 285 private void writeApplyAction( Writer writer, ApplyAction action, String pad ) throws IOException 286 { 287 String id = action.getID(); 288 writer.write( "\n" + pad + "<apply id=\"" + id + "\"/>" ); 289 } 290 291 private void writeNestedState( Writer writer, State state, String pad ) throws IOException 292 { 293 String name = state.getName(); 294 writer.write( "\n" + pad + "<state name=\"" + name + "\"" ); 295 if( state.isTerminal() ) 296 { 297 writer.write( " terminal=\"true\">" ); 298 } 299 else 300 { 301 writer.write( ">" ); 302 } 303 writeBody( writer, state, pad + " " ); 304 writer.write( "\n" + pad + "</state>" ); 305 } 306 307 private boolean isEmpty( State state ) 308 { 309 if( state.getTriggers().length > 0 ) 310 { 311 return false; 312 } 313 if( state.getTransitions().length > 0 ) 314 { 315 return false; 316 } 317 if( state.getOperations().length > 0 ) 318 { 319 return false; 320 } 321 if( state.getInterfaces().length > 0 ) 322 { 323 return false; 324 } 325 if( state.getStates().length > 0 ) 326 { 327 return false; 328 } 329 return true; 330 } 331 }